﻿var dlTabsTab = {
	
	// ====================================== Init Functions ======================================
	
	init: function(e)
	{
		if(!this.initialized)
		{
			this.initialized = true;
			
			consoleLog("tab init");
			
			// Unpack parameters
			var array = window.location.href.split("?")[1].split("&");
			var hash = new Array();
			
			array.forEach(
				function(each)
				{
					hash[each.split("=")[0]] = each.split("=")[1];
				}
			);
			
			// Get the id
			this.id = parseInt(hash["id"]);
			
			// Get the main window holding this tab
			this.mainWindow = window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                       .getInterface(Components.interfaces.nsIWebNavigation)
                       .QueryInterface(Components.interfaces.nsIDocShellTreeItem)
                       .rootTreeItem
                       .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                       .getInterface(Components.interfaces.nsIDOMWindow);
			
			// Find the tab holding this page
			this.findTab();
			
			// Find if and when the time is closing automatically
			this.closeTime = this.tab.getAttribute("dlTabsTab_closeTime");
			if(!this.closeTime)
				this.closeTime = -2;
			//consoleLog("closeTime: " + this.closeTime);
			
			// Register this download monitor
			this.dm = Components.classes["@mozilla.org/download-manager;1"]
							.getService(Components.interfaces.nsIDownloadManager);
			this.dm.addListener(this);
			
			// Get the locale
			this.strbundle = document.getElementById("tabStrings");
			//alert("this.strbundle: " + this.strbundle.getString);
									
			// Get misc info from download
			try
			{
				this.download = this.dm.getDownload(this.id);
			}
			// Download not found
			catch(ex)
			{
				this.download = 0;
			}
			if(this.download)
			{
				this.file = (this.download.targetFile || this.download.target);
				this.name = this.download.displayName;
				this.source = this.download.source.host;
				this.localpath = this.file.path;
				this.URL = this.download.source.prePath + this.download.source.path;
				this.folder = this.file.parent.path;
				this.lastAmount = this.download.amountTransferred;
			}
			else
			{
				this.file = 0;
				this.name = this.strbundle.getString("noSuchDownload");
				this.source = "";
				this.localpath = "";
				this.URL = "";
				this.folder = "";
				this.lastAmount = 0;
			}
			
			this.time = Date.now();
			
			// Set preferences up
			dlTabsPrefs.init();
			
			// Set various listeners up
			this.setupListeners();
			
			this.tooltip = new dlTabsTabTooltip();
			this.page = new dlTabsTabPage();
			this.tabContent = new dlTabsTabContent();
			
			// Wait for XBL changes to propagate
			var timer = Components.classes["@mozilla.org/timer;1"]
								.createInstance(Components.interfaces.nsITimer);
			var event = { notify: function(timer)
				{
					dlTabsTab.refresh();
				}
			}
			timer.initWithCallback(event,
								100,
								Components.interfaces.nsITimer.TYPE_ONE_SHOT);
		}
	},
	
	setupListeners : function()
	{
		// Double-click listener, to launch file
		this.launching = false;
		this.mainWindow.gBrowser.mStrip.addEventListener("dblclick",
			function(e)
			{
				// Not found or finished?  Forget it
				if(!dlTabsTab.download || dlTabsTab.download.state != Components.interfaces.nsIDownloadManager.DOWNLOAD_FINISHED)
					return;
				// If the tab is the same tab
				if(e.target == dlTabsTab.tab)
				{
					dlTabsTab.launch();
				}
			},
			false);
		
		// Overlay may also send a MessageEvent because the context menu launched
		this.tab.addEventListener("dlTabs_launch",
			function(e)
			{
				dlTabsTab.launch();
			},
			false);
		
		// Overlay may also make this tab close
		this.tab.addEventListener("dlTabs_close",
			function(e)
			{
				window.close();
			},
			false);
					
		// Resize listener of the tab box, for the progress bar
		this.mainWindow.gBrowser.mTabBox.addEventListener("resize",
			function(e)
			{
				dlTabsTab.refresh();
			},
			false);
		
		// Refresh in case of stall, but only if the download is found
		if(this.download)
		{
			this.autoRefreshTimer = Components.classes["@mozilla.org/timer;1"]
								.createInstance(Components.interfaces.nsITimer);
			var event = { notify: function(timer)
				{
					if(dlTabsPrefs.getAutoRefresh() && Date.now() - dlTabsTab.time > 3000)
						dlTabsTab.refresh();
				}
			}
			this.autoRefreshTimer.initWithCallback(event,
								3000,
								Components.interfaces.nsITimer.TYPE_REPEATING_SLACK);
		}
			
		// Listener for tab movement, to refreh the labels in the tab
		this.mainWindow.gBrowser.tabContainer.addEventListener("TabMove",
			function(e)
			{
				if(e.target == dlTabsTab.tab)
				{
					dlTabsTab.refresh();
				}
			},
			false);
	},
	
	// ====================================== Download Progress Listener ======================================
	
	onProgressChange : function(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress, aDownload)
	{
		if(aDownload == this.download)
		{
			this.refresh();
		}
    },
	
	onDownloadStateChange : function(aState, aDownload)
	{
		if(aDownload == this.download)
		{
			// Refresh
			this.refresh();
		}
    },
	
	// Ignore these
	onSecurityChange : function(aWebProgress, aRequest, aState, aDownload)
	{
    },
	
    onStateChange : function(aWebProgress, aRequest, aStateFlags, aStatus, aDownload)
	{
    },
	
	// ====================================== Refresh ======================================
	
	refresh : function()
	{
		this.refreshETA();
		this.refreshSpeed();
		this.refreshSize();
		this.refreshProgress();
		
		this.addTabLabels();
		
		consoleLog("start tab content");
		this.tabContent.refresh();
		consoleLog("start page");
		this.page.refresh();
		consoleLog("start tooltip");
		this.tooltip.refresh();
		
		consoleLog("GREAT SUCCESSSSSSSSSSSS!!!!!!!!");
		
		this.filterCloseCheck();
	},
	
	filterCloseCheck : function()
	{
		if(this.download.state == Components.interfaces.nsIDownloadManager.DOWNLOAD_FINISHED 
			&& this.closeTime >= 0)
		{
			//consoleLog("filterClose: " + this.closeTime);
		
			this.autoRefreshTimer.cancel();
			
			// Immediate execution
			if(this.closeTime == 0)
				window.close();
			// Slow excrutiation torture
			else if(this.closeTime > 0)
			{
				var timer = Components.classes["@mozilla.org/timer;1"]
								.createInstance(Components.interfaces.nsITimer);
				var event = { notify: function(timer)
					{
						window.close();
					}
				}
				timer.initWithCallback(event,
									this.closeTime * 1000,
									Components.interfaces.nsITimer.TYPE_ONE_SHOT);
			}
		}
	},
	
	refreshSpeed : function()
	{
		if(!this.download)
		{
			this.speed = "";
			return;
		}
		
		var deltaSize = this.download.amountTransferred - this.lastAmount;
		this.lastAmount = this.download.amountTransferred;
		
		var deltaTime = Date.now() - this.time;
		//alert("time: " + Date.now() + ", " + this.time);
		this.time = Date.now();
		
		if(deltaTime == 0)
			this.speedNum = 0;
		else
			this.speedNum = deltaSize / deltaTime * 1000;
			
		this.speed = sizeReadable(this.speedNum) + "/s";
	},
	
	refreshETA : function()
	{
		this.ETA = "";
		
		if(!this.download)
		{
			return;
		}
		
		// Launching the file
		if(this.launching)
		{
			this.ETA = this.strbundle.getString("launching");
		}
		else
		{
			switch(this.download.state)
			{
				case Components.interfaces.nsIDownloadManager.DOWNLOAD_DOWNLOADING:
					// Unknown size
					if(this.download.size == 0)
					{
						this.ETA = this.strbundle.getString("unknown");
					}
					// Not stalled
					else if(this.speedNum)
					{
						var x = Math.floor((this.download.size - this.download.amountTransferred) / this.speedNum);
						if (x < 1)
							this.ETA = this.strbundle.getString("almostDone")
						else
						{
							/* Convert the time to a readable format
							 * This complex setup with a stack is basically just
							 * for finding the first nonzero time unit
							 */
							var timeArray = new Array();
							
							var seconds = x % 60
							if(seconds)			timeArray.push(this.strbundle.getFormattedString("second", [seconds]) + " ");
							x = Math.floor(x / 60);
							var minutes = x % 60
							if(minutes)			timeArray.push(this.strbundle.getFormattedString("minute", [minutes]) + " ");
							x = Math.floor(x / 60);
							var hours = x % 24
							if(hours)			timeArray.push(this.strbundle.getFormattedString("hour", [hours]) + " ");
							x = Math.floor(x / 24);
							var days = x
							if(days)			timeArray.push(this.strbundle.getFormattedString("day", [days]) + " ");
							
							while(timeArray.length)
								this.ETA += timeArray.pop();
							
							this.ETA = this.strbundle.getFormattedString("timeLeft", [this.ETA]);
						}
					}
					else
						this.ETA = this.strbundle.getString("stalled");
					break;
				case Components.interfaces.nsIDownloadManager.DOWNLOAD_NOTSTARTED:		this.ETA = this.strbundle.getString("starting");	break;
				case Components.interfaces.nsIDownloadManager.DOWNLOAD_SCANNING:		this.ETA = this.strbundle.getString("scanning");	break;
				case Components.interfaces.nsIDownloadManager.DOWNLOAD_BLOCKED:			this.ETA = this.strbundle.getString("blocked");		break;
				case Components.interfaces.nsIDownloadManager.DOWNLOAD_QUEUED:			this.ETA = this.strbundle.getString("queued");		break;
				case Components.interfaces.nsIDownloadManager.DOWNLOAD_FINISHED:		this.ETA = this.strbundle.getString("finished");	break;
				case Components.interfaces.nsIDownloadManager.DOWNLOAD_PAUSED:			this.ETA = this.strbundle.getString("paused");	break;
				case Components.interfaces.nsIDownloadManager.DOWNLOAD_FAILED:			this.ETA = this.strbundle.getString("failed");	break;
				case Components.interfaces.nsIDownloadManager.DOWNLOAD_CANCELED:		this.ETA = this.strbundle.getString("canceled");	break;
			};
		}
	},
	
	refreshSize : function()
	{
		if(!this.download)
		{
			this.size = "";
			return;
		}
		
		this.size = sizeReadable(dlTabsTab.download.amountTransferred) + " / " +
					sizeReadable(dlTabsTab.download.size);
	},
	
	refreshProgress : function()
	{
		if(!this.download)
		{
			this.progress = 0;
			this.rounded = "";
		}
		else if(this.download.size == 0)
		{
			this.progress = 0;
			this.rounded = this.strbundle.getString("unknown");
		}
		else
		{
			// more precise percentage
			this.progress = 100 * this.download.amountTransferred / this.download.size;
			
			// Round percent
			var rounding = dlTabsPrefs.getDisplayPref("percentRound");
			
			this.rounded = this.progress + "";
			if(this.rounded.length > rounding)
				this.rounded = this.rounded.substring(0, rounding);
		}
	},
	
	// ====================================== Utility Functions ======================================
	
	// Adds the additional labels
	addTabLabels : function()
	{
		consoleLog("addTabLabels");
		this.tabLabelBox = getXBLElementsByClassName(this.tab.ownerDocument, this.tab, "dlTabs_labelBox", new Array());
		if(!this.tabLabelBox || this.tabLabelBox.length == 0)
		{
			var tabHBox = this.mainWindow.document.createElement("xul:hbox");
			tabHBox.style.MozBinding = "url(chrome://downloadtabs/content/tab.xbl#hboxTab)";
		
			var tabImageMiddle = getXBLElementsByClassName(this.tab.ownerDocument, this.tab, "tab-image-middle", new Array());
			if(tabImageMiddle && tabImageMiddle.length > 0)
			{
				consoleLog("tabImageMiddle (" + tabImageMiddle.length + "): " + tabImageMiddle[0]);
				tabImageMiddle[0].appendChild(tabHBox);
			}
			else
			{
				consoleLog("nothing called tabImageMiddle; applying default XBL binding");
				this.tab.style.MozBinding = "url(chrome://downloadtabs/content/tab.xbl#tab)";
			}
		}
		this.tabLabelBox = getXBLElementsByClassName(this.tab.ownerDocument, this.tab, "dlTabs_labelBox", new Array());
	},
	
	// Find the tab object of this tab
	findTab : function()
	{
		var tabs = this.mainWindow.gBrowser.mTabBox.tabs
		var tab, tabBrowser;
		for(var i = 0; i < tabs.itemCount; i++)
		{
			tab = tabs.getItemAtIndex(i);
			tabBrowser = this.mainWindow.gBrowser.getBrowserForTab(tab);
			if(tabBrowser.contentDocument == document)
			{
				this.tab = tab;
				// Set attributes on this so the browser overlay knows which download to modify
				this.tab.setAttribute("dlTabsTab", true);
				this.tab.setAttribute("dlTabsTab_dl", this.id);
				
				// Do the XBL binding ASAP because there's a delay
				this.addTabLabels();
				
				return;
			}
		}
	},
	
	// Launch the file, close this tab
	launch : function()
	{
		// Notify refreshETA
		this.launching = true;
		this.refresh();
		// Launch
		this.file.launch();
		// Close
		window.close();
	}
};

window.addEventListener("load", 
	function(e)
	{
		dlTabsTab.init(e);
	},
	false);